import { Camera, Component, director, game, instantiate, Label, Layers, macro, Node, Prefab, rect, RenderTexture, ResolutionPolicy, setDisplayStats, size, Size, Sprite, SpriteFrame, sys,  tween, UITransform, v2, view, Widget } from "cc";
import { App } from "../App";
import { Singleton, EventConst } from "./Common";

const COMMON:string = 'Common';
interface LoadItem {
    path:string,
    parent:Node,
    cls:string,
    show:boolean,
    call?:(node:Node)=>void,
    args: any
}
export default class sceneMgr extends Singleton<sceneMgr>{
    private _nextScene:Component = null;
    private _curScene:Component = null;
    private _sceneLayout:Node = null;
    private _viewLayout:Node = null;
    private _maskLayout:Node = null;
    private _topLayout:Node = null;
    private _waitNode:Node = null;
    private _waitText:Label = null;
    private _isSingleScene:boolean = true;
    private _nodesMap:Map<string,Map<string,Node>> = null;
    private _isLoading:boolean = false;
    private _loadingArray:LoadItem[] = [];
    private _loadingTime:any = null;
    private _isLandscape:boolean = true;

    protected onInit(){
        this._nodesMap = new Map<string,Map<string,Node>>();
        this._nodesMap.set(COMMON,new Map<string,Node>())
        this._isSingleScene = App.getValue('singleScene');
        this._initView();

        App.Event.on(EventConst.ENTRY_SCENE, this._entryScene, this);
        App.Event.on(EventConst.OPEN_VIEW, this._openView, this);
        App.Event.on(EventConst.CLOSE_VIEW, this._closeView, this);
        App.Event.on(EventConst.JUST_NODE_SIZE, this._adjustmentNode, this);
        App.Event.on(EventConst.LOCK_SCREEN, this._wait, this);

        if (sys.isBrowser){
            view.setResizeCallback(()=>{
                director.getScene().getComponentsInChildren(Widget).forEach((widget:Widget)=>{
                    widget.updateAlignment();
                });
            });
        }

        setDisplayStats(App.getValue('showFPS'));
        const fps = App.getValue('lockFPS') || 30;
        game.frameRate = fps;
        const design:Size = view.getDesignResolutionSize();
        view.setResolutionPolicy(design.width > design.height ? ResolutionPolicy.FIXED_HEIGHT : ResolutionPolicy.FIXED_WIDTH);

        /*const mask:Node = director.getScene().getChildByPath('RootNode/Canvas/ViewNode/MaskNode') as any;
        if (App.isValid(mask)){
            (mask.getComponent(Sprite) || mask.addComponent(Sprite)).spriteFrame = this.saveScene();
        }*/
    }

    public setOrientation(isLandscape:boolean){
        if (this._isLandscape === isLandscape) return ;
        if (sys.isNative){
            //App.Native.callNative('setOrientation','',null,`${isLandscape}`);
        } else {
            const values = isLandscape ? macro.ORIENTATION_LANDSCAPE : macro.ORIENTATION_PORTRAIT;
            view.setOrientation(values);
            setTimeout(() => {
                this._isLandscape = isLandscape;
                if (!sys.isNative){
                    view.setResizeCallback(()=>{
                        director.getScene().getComponentsInChildren(Widget).forEach((widget:Widget)=>{
                            widget.updateAlignment();
                        });
                    });
                }
            }, 100);
        }
    }

    /**
     * 加载一个预制体，并挂接到给定的父节点上
     * @param path 预制体路劲
     * @param parent 欲挂接父节点
     * @param cls 预制体组件类名
     * @param call 处理结束回调
     * @param args 初始化参数
     */
    public addTo(path:string,parent:Node,show:boolean=true,call?:(node:Node)=>void,...args: any){
        if (App.isEmpty(path)) return ;
        for (let it of this._loadingArray){
            if (path === it.path){
                return ;
            }
        }

        this._loadingArray.push({
            path:path,
            parent:parent,
            cls:path.substring(path.lastIndexOf('/')+1,path.length),
            show:show,
            args:[args[0],args[1],args[2],args[3],args[4]]});
        if (this._isLoading){
            return ;
        }
        if (this._loadingTime !== null){
            clearInterval(this._loadingTime);
            this._loadingTime = null;
        }
        this._loadingTime = setInterval(()=>{
            if (!this._isLoading){
                this._isLoading = true;
                let item:LoadItem = this._loadingArray.shift();
                const subs:string[] = item.path.split(':');
                const key:string = subs.length < 2 ? 'resources' : subs[1];
                let map:Map<string,Component|Node> = this._nodesMap.get(key);
                !App.isValid(map) && (map = new Map<string,Component>());
                const obj = map.get(item.path);
                if (App.isValid(obj)){
                    call && call(obj instanceof Component ? obj.node:obj);
                    return ;
                }
        
                App.Asset.load(item.path,(prefab:Prefab)=>{
                    const node:Node = instantiate(prefab);
                    if (App.isValid(parent)){
                        if (parent instanceof Component) {
                            parent = parent.node;
                        }
                        if (this._loadingArray.length < 1){
                            clearInterval(this._loadingTime);
                            this._loadingTime = null;
                        }
                        this._isLoading = false;
                        if (!App.isEmpty(item.cls)){
                            const obj = node.getComponent(item.cls);
                            if (App.isValid(obj)){
                                // @ts-ignore
                                obj.init && obj.init(item.args[0],item.args[1],item.args[2],item.args[3],item.args[4]);
                            }
                        }
                        node.active = item.show;
                        node.parent = parent;
                        call && call(node);
                    }
                },Prefab);
            }
        },100);
    }

    public saveScene(file?:string):SpriteFrame{
        // 先将3D场景绘制到UI层
        const canvasSize:Size = view.getVisibleSize();
        const tmpSprite:Sprite = (new Node('tmpSprite')).addComponent(Sprite);
        tmpSprite.node.layer = Layers.Enum.UI_2D;
        App.contentSize(tmpSprite,canvasSize);
        const parent:Node = director.getScene().getChildByPath('RootNode/Canvas') as any;
        parent.addChild(tmpSprite.node);
        tmpSprite.node.setSiblingIndex(0);
        const sp = new SpriteFrame();
        sp.reset({
            originalSize: canvasSize,
            rect: rect(0,0,1,1),
            offset: v2(0,0),
            isRotate: true,
            borderTop: 0,
            borderLeft: 0,
            borderBottom: 0,
            borderRight: 0,
        });
 
        const renderTex = new RenderTexture();
        renderTex.reset(canvasSize);
        const camera:Camera = director.getScene().getChildByName('MainCamera').getComponent(Camera);
        camera.targetTexture = renderTex;
        sp.texture = renderTex;
        sp.flipUVY = true;
        tmpSprite.spriteFrame = sp;

        // 截屏操作
        const UICamera:Camera = parent.getChildByName('Camera').getComponent(Camera);
        const newSpFrame = new SpriteFrame();
        const texture = new RenderTexture();
        texture.reset(view.getVisibleSize());
        UICamera.targetTexture = texture;
        newSpFrame.texture = texture;
        newSpFrame.flipUVY = true;
        game.step();
        parent.removeChild(tmpSprite.node);
        tmpSprite.destroy();
        camera.targetTexture = null;
        UICamera.targetTexture = null;
        return newSpFrame;
    }

    private _initView(){
        const root:Node = (director.getScene().getChildByName('RootNode') as any);
        this._sceneLayout = root.getChildByPath('Canvas/SceneNode');
        this._viewLayout = root.getChildByPath('Canvas/ViewNode');
        this._maskLayout = root.getChildByPath('Canvas/MaskNode');
        this._topLayout = root.getChildByPath('Canvas/TopNode');
        this._maskLayout.active = false;
        const coms:string[] = ['prefabs/views/Tips','prefabs/views/MessageBox'];
        App.Asset.waitLoad(coms,(file:string,prefab:Prefab)=>{
            const node:Node = instantiate(prefab);
            if (App.isValid(node)){
                node.active = false;
                this._topLayout.addChild(node);
                const cls = node.getComponent(file.substring(file.lastIndexOf('/')+1,file.length));
                // @ts-ignore
                (cls && cls.init) && cls.init();
                this._nodeInsetMap(COMMON,file,node);
            }
        });
    }

    private _entryScene(scene: string, ...args: any) {
        this._wait(true);
        if (this._isSingleScene){
            let subs = scene.split(':');
            //return ;
            this.addTo(`prefab/scene/${scene}`,this._sceneLayout,true,(node:Node)=>{
                if (App.isValid(this._curScene)){
                    this._curScene.getComponent(UITransform).priority = 1;
                }
                
                this._adjustmentNode(node);
                node.getComponent(UITransform).priority = 0;
                if (App.isValid(this._curScene)){
                    this._curScene.node.parent = null;
                    this._curScene.destroy();
                    App.Event.offAll(this._curScene);
                }
                this._curScene = node.getComponent(subs[1]||scene);
                this._wait(false);
            },args[0],args[1],args[2],args[3],args[4]);
        } else {
            //this._initLayout(mainScene);
        }
        this._wait(false);
    }

    private _openView(name: string, ...args: any) {
        if (App.isEmpty(name)){
            console.error('viewMgr.openView is error.');
            return ;
        }

        this.saveScene();

        this._viewLayout.active = true;
        const subs:string[] = name.split(':');
        const path:string = subs.length > 1 ? `${subs[1]}/res/prefabs/views/${subs[0]}`:`prefabs/views/${subs[0]}`;
        console.time(`open:${path}`);
        let tmpNode:Node = this._findNode(path);
        if (App.isValid(tmpNode)){
            this._showView(tmpNode,true,subs[0],args[0],args[1],args[2],args[3],args[4]);
            console.time(`open:${path}`);
            return ;
        }
        
        App.Asset.load(path,(res:Prefab)=>{
            tmpNode = instantiate(res);
            // @ts-ignore
            this._showView(tmpNode,true,args[0],args[1],args[2],args[3],args[4]);
            console.time(`open:${path}`);
        },Prefab);
    }

    private _closeView(view: Node | Component, ...args: any) {
        // @ts-ignore
        view && this._showView(view.node || view,false);
    }

    private _nodeInsetMap(key:string,name:string,node:Node){
        let map = this._nodesMap.get(key);
        !App.isValid(map) && (map = new Map<string,Node>());
        !App.isValid(map.get(name)) && map.set(name,node);
    }

    private _adjustmentNode(node:Node|Component){
        if (!App.isValid(node)) return ;

        if (node instanceof Component) node = node.node;
        const ui = node.getComponent(UITransform);
        let tmpSize:Size = ui.contentSize;
        const wnd:Size = size(screen.width,screen.height);
        tmpSize.width = Math.min(tmpSize.width,wnd.width);
        tmpSize.height = Math.min(tmpSize.height,wnd.height);
        ui.contentSize = tmpSize;
        node.getComponentsInChildren(Widget).forEach((widget:Widget)=>{
            widget.updateAlignment();
        });
    }

    private _wait(wait:boolean,key?:string){
        App.opacity(this._waitNode,0);
        this._maskLayout.active = wait;
        tween(this._maskLayout)
        .call(()=>{
            wait && (this._waitText.string = App.isEmpty(key)?'':App.i18n.getString(key));
            this._waitNode.active = wait;
            App.opacity(this._waitNode,wait?255:0,wait?0.1:0);
        })
        .delay(wait?30:0)
        .call(()=>{
            App.opacity(this._waitNode,0,0);
            this._waitNode.active = false;
            this._maskLayout.active = false;
            this._waitText.string = '';
        })
        .start();
    }

    private _findNode(name:string):Node{
        const subs:string[] = name.split(':');
        const map = this._nodesMap.get(subs.length > 1 ? subs[1]:COMMON);
        for (let it of map){
            if (it[1].name === subs[0]){
                return it[1];
            }
        }
        return null;
    }

    private _showView(node:Node,show:boolean,...args: any) {
        if (!App.isValid(node)) return ;

        App.opacity(node,show?255:0,show?0.1:0,()=>{
            const cls = node.getComponent(node.name);
            if (show){
                node.parent = this._viewLayout;
                // @ts-ignore
                cls && cls.init && cls.init(args[0],args[1],args[2],args[3],args[4]);
            } else {
                // @ts-ignore
                cls && cls.release && cls.release(args[0],args[1],args[2],args[3],args[4]);
                this._viewLayout.removeAllChildren();
                this._viewLayout.active = false;
                this._viewLayout.getComponent(Sprite).spriteFrame = null;
            }
        });
    }
}